package com.zzyl.security;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zzyl.constant.SecurityConstant;
import com.zzyl.constant.UserCacheConstant;
import com.zzyl.properties.JwtTokenManagerProperties;
import com.zzyl.utils.EmptyUtil;
import com.zzyl.utils.JwtUtil;
import com.zzyl.utils.UserThreadLocal;
import com.zzyl.vo.UserVo;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * 自定义授权管理器
 */
@Slf4j
@Component
public class JwtAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private JwtTokenManagerProperties jwtTokenManagerProperties;

    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication,
                                       RequestAuthorizationContext requestAuthorizationContext) {
        // 获取用户当前请求路径
        // 获取当前请求的 HTTP 方法（如 GET、POST 等）
        String method = requestAuthorizationContext.getRequest().getMethod();
        // 获取当前请求的 URI
        String requestURI = requestAuthorizationContext.getRequest().getRequestURI();
        // 组合出请求的目标 URL
        String targetUrl = (method + requestURI);

        // 从请求头中获取 userToken（UUID），表示用户的身份验证信息
        String userToken = requestAuthorizationContext.getRequest().getHeader(SecurityConstant.USER_TOKEN);

        // 如果 userToken 为空，则返回403
        if (EmptyUtil.isNullOrEmpty(userToken)) {
            return new AuthorizationDecision(false);
        }

        // 获取 jwtToken
        // 通过 userToken 获取相应的 jwtToken 在 Redis 中的键名，从 Redis 中获取该 jwtToken
        String jwtTokenKey = UserCacheConstant.JWT_TOKEN + userToken;
        String jwtToken = redisTemplate.opsForValue().get(jwtTokenKey);

        // 如果 jwtToken 为空，则返回403
        if (EmptyUtil.isNullOrEmpty(jwtToken)) {
            return new AuthorizationDecision(false);
        }

        // 解析 jwt，并验证 jwtToken 的有效性
        Claims cla = JwtUtil.parseJWT(jwtTokenManagerProperties.getBase64EncodedSecretKey(), jwtToken);
        // 如果解析结果为空，表示 token 无效或过期，返回403
        if (ObjectUtil.isEmpty(cla)) {
            return new AuthorizationDecision(false);
        }

        // 从 claims 中获取当前用户信息，并转换为 UserVo 对象
        UserVo userVo = JSONObject.parseObject(String.valueOf(cla.get("currentUser")), UserVo.class);

        // 从 Redis 中获得最新的 userToken（UUID），判断当前用户是否被其他用户剔除
        //如果redis中最新的userToken与出入的userToken不符合，则认为当前用户被后续用户剔除
        //key:username  value：uuid
        String currentUserToken = redisTemplate.opsForValue().get(UserCacheConstant.USER_TOKEN + userVo.getUsername());
        // 如果两个 token 不相同，则返回403
        if (!userToken.equals(currentUserToken)) {
            return new AuthorizationDecision(false);
        }

        // token续期
        // 检查 jwtToken 的存活时间是否少于 10分钟
        Long remainTimeToLive = redisTemplate.opsForValue().getOperations().getExpire(jwtTokenKey);
        if (remainTimeToLive.longValue() <= 600) {
            // 需要进行续期，准备新的 claims
            Map<String, Object> claims = new HashMap<>();
            String userVoJsonString = String.valueOf(cla.get("currentUser"));
            claims.put("currentUser", userVoJsonString);

            // 生成新的 jwtToken
            String newJwtToken = JwtUtil.createJWT(jwtTokenManagerProperties.getBase64EncodedSecretKey(), jwtTokenManagerProperties.getTtl(), claims);
            // 设置新的 token 的持久时间
            long ttl = Long.valueOf(jwtTokenManagerProperties.getTtl()) / 1000;

            // 更新 Redis 中的 jwtToken 和 userToken
            redisTemplate.opsForValue().set(jwtTokenKey, newJwtToken, ttl, TimeUnit.SECONDS);
            redisTemplate.expire(UserCacheConstant.USER_TOKEN + userVo.getUsername(), ttl, TimeUnit.SECONDS);
        }

        // 检查用户是否对 targetUrl 具备授权访问权限
        /*for (String resourceRequestPath : userVo.getResourceRequestPaths()) {
            boolean isMatch = antPathMatcher.match(resourceRequestPath, targetUrl);
            if (isMatch){
                log.info("用户:{}拥有targetUrl权限:{}==========",userVo.getUsername(),targetUrl);
                return new AuthorizationDecision(true);
            }
        }*/

        return new AuthorizationDecision(true);
    }

}
